package com.lvtu.monitor.notification.mail;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.elasticsearch.common.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.lvmama.com.lvtu.common.utils.DateUtil;
import com.lvmama.com.lvtu.common.utils.MemcachedUtil;
import com.lvtu.monitor.dao.exmatch.po.ExDetail;
import com.lvtu.monitor.dao.exmatch.po.ExPattern;
import com.lvtu.monitor.mongo.service.IBaseMongoService;
import com.lvtu.monitor.utils.Constants;
import com.lvtu.monitor.utils.mail.SimpleMailSender;

/**
 * @Title: ExObserver.java
 * @Package com.lvtu.monitor.notification.mail
 * @Description: 异常监控的观察者
 * @author dengcheng
 * @date 2015-5-22 下午1:50:45
 * @version V1.0.0
 */

@Repository
public class ExObserver implements Observer {

	private static Logger log = Logger.getLogger(ExObserver.class);

	@Autowired
	private IBaseMongoService mongoService;

	private static final String KEY_PATTERN_LAST_RUN_PREFIX = "lvtu_monitor_pattern_last_run_";

	@Override
	public void update(Observable o, Object arg) {
		ExObservable observable = (ExObservable) o;

		log.info("observed object change..."
				+ observable.getEx().getException().substring(0, 20));
		// 从缓存或者mongodb中取全部监控规则
		List<ExPattern> exPatterns = this.getExPatterns();

		for (ExPattern exPattern : exPatterns) {
			// 判断规则是否可用
			if (!this.isPatternRunning(exPattern)) {
				continue;
			}
			ExDetail exDetail = observable.getEx();
			// 异常是否匹配规则
			boolean match = this.isMatchPattern(exPattern.getPattern(),
					exDetail);
			if (match) {
				// 保存运行时间
				this.saveLastRunTime(exPattern);
				// 发送邮件
				this.sendEmail(exPattern, exDetail);
			}
		}
	}

	/**
	 * 获取全部异常监控规则
	 * 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private List<ExPattern> getExPatterns() {
		Object cachedObj = MemcachedUtil.getInstance().get(
				Constants.KEY_ALL_EX_PATTERNS);
		List<ExPattern> exPatterns = null;
		if (cachedObj != null) {
			exPatterns = (List<ExPattern>) cachedObj;
		} else {
			exPatterns = mongoService.findAll(ExPattern.class);
			MemcachedUtil.getInstance().set(Constants.KEY_ALL_EX_PATTERNS,
					exPatterns);
		}
		return exPatterns;
	}

	/**
	 * 判断规则是否生效
	 * 
	 * @param exPattern
	 * @return
	 */
	private boolean isPatternRunning(ExPattern exPattern) {
		if (!exPattern.isValid()) {
			return false;
		}
		// Long lastRunTime = exPattern.getLastRunTime();

		String key = KEY_PATTERN_LAST_RUN_PREFIX + exPattern.get_id();
		Object cachedObj = MemcachedUtil.getInstance().get(key);
		if (cachedObj == null) {
			return true;
		}
		Long lastRunTime = (Long) cachedObj;
		int interval = exPattern.getIntervalMinutes();
		if (lastRunTime == null || interval == 0) {
			return true;
		}
		// 如果最后一次运行时间到现在,超过了设置的间隔时间,则执行pattern
		if (new Date().getTime() - interval * 1000 * 60 > lastRunTime) {
			return true;
		}
		return false;
	}

	/**
	 * 判断规则是否匹配
	 * 
	 * @param patternStr
	 * @param ex
	 * @return
	 */
	private boolean isMatchPattern(String patternStr, ExDetail ex) {

		// 只匹配异常的规则
		if (!patternStr.contains(" ")) {
			return this.patternMatch(patternStr, ex.getException());
		}

		// 添加了多个规则匹配
		String[] rulePairs = patternStr.split(ExPattern.RULE_SEPERATOR);

		for (String rulePair : rulePairs) {

			String[] ruleDetail = rulePair.split(" ");
			if (ruleDetail.length != 3) {
				return false;
			}
			String field = rulePair.split(" ")[0];
			String matchType = rulePair.split(" ")[1];
			String ruleValue = rulePair.split(" ")[2];

			String exValue = this.getValueByField(ex, field);
			if (ExPattern.MatchType.eq.name().equals(matchType)) {
				// 精确匹配
				if (!StringUtils.equals(exValue, ruleValue)) {
					return false;
				}
			} else if (ExPattern.MatchType.like.name().equals(matchType)) {
				// 正则匹配
				if (!this.patternMatch(ruleValue, exValue)) {
					return false;
				}
			} else {
				// 其它matchtype
			}
		}

		return true;
	}

	/**
	 * 判断字符串是否匹配正则
	 * 
	 * @param patternStr
	 * @param str
	 * @return
	 */
	private boolean patternMatch(String patternStr, String str) {
		try {
			Pattern pattern = Pattern.compile(patternStr);
			Matcher matcher = pattern.matcher(str);
			return matcher.find();
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 反射获取字段值
	 * 
	 * @param ex
	 * @param fieldName
	 * @return
	 */
	private String getValueByField(ExDetail ex, String fieldName) {
		try {
			PropertyDescriptor pd = new PropertyDescriptor(fieldName,
					ex.getClass());
			Method getMethod = pd.getReadMethod();
			String value = getMethod.invoke(ex, null).toString();
			return value;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 保存执行pattern的时间
	 * 
	 * @param exPattern
	 */
	private void saveLastRunTime(ExPattern exPattern) {
		// Map<String, Object> update = new HashMap<String, Object>();
		// update.put("lastRunTime", new Date().getTime());
		// mongoService.updateOne(exPattern.get_id(), update, ExPattern.class);

		// pattern从缓存中取,所以不能用mongodb里的上次运行时间了
		String key = KEY_PATTERN_LAST_RUN_PREFIX + exPattern.get_id();
		MemcachedUtil.getInstance().set(key, new Date().getTime());
	}

	/**
	 * 发送异常报警邮件
	 * 
	 * @param exPattern
	 * @param exDetail
	 */
	private void sendEmail(ExPattern exPattern, ExDetail exDetail) {
		log.info("error message match the pattern:"
				+ exPattern.getPatternName());
		String emailAddrs = exPattern.getEmail();
		if (StringUtils.isEmpty(emailAddrs)) {
			return;
		}
		for (String emailAddr : emailAddrs.split(",")) {
			log.info("send mail to " + emailAddr);
			String emailBody = this.generateEmail(exDetail);
			SimpleMailSender.sendHtmlMail(emailAddr, emailBody,
					exPattern.getPatternName());
		}
	}

	/**
	 * 生成邮件正文
	 * 
	 * @param exDetail
	 * @return
	 */
	private String generateEmail(ExDetail exDetail) {
		StringBuilder builder = new StringBuilder();
		builder.append("<h4>参数:</h4>").append(exDetail.getQueryString())
				.append("<br/>");
		String dateStr = DateUtil.formatDate(
				new Date(exDetail.getCreatedTime()), "yyyy-MM-dd HH:mm:ss");
		builder.append("<h4>时间:</h4>").append(dateStr).append("<br/>");
		builder.append("<h4>ip:</h4>").append(exDetail.getIp()).append("<br/>");
		builder.append("<h4>userAgent:</h4>").append(exDetail.getUserAgent())
				.append("<br/>");
		builder.append("<h4>异常信息:</h4>")
				.append("<pre>")
				.append(exDetail.getException())
				.append("</pre>")
				.append("<br/>");
		return builder.toString();
	}

}
